/*
 * @Descripttion: 绘制水平方向多个矩形区域
 * @version: V1.0.0.1
 * @Author: zhaozhenzhuo
 * @Date: 2025-10-27 10:09:06
 * @LastEditors: zhaozhenzhuo
 * @LastEditTime: 2025-10-27 10:09:06
 */

/**
 * 绘制水平方向多个矩形区域。
 * 功能描述:
 *     创建画布（计算坐标缩放值）；
 *     设置绘制模式；
 *     设置样式；
 *     在指定坐标处绘制矩形；
 */
import { formatTime } from '@/assets/js/utils';
import type { AudioTaskTimeTableItemType } from '@/api/service/lesson-analysis-details.type';

// 块信息
interface BlockInfoType extends AudioTaskTimeTableItemType {
	x: number;
	y: number;
	width: number;
	height: number;
	index: number;
	color: string;
}

export default class {
	// 容器dom元素
	canvasWrapper: HTMLDivElement | null = null;
	// 手动创建的包裹元素
	areaCanvasWrap: HTMLDivElement | null = null;
	// 画布dom元素
	canvas: HTMLCanvasElement | null = null;
	// 画布上下文对象
	ctx: CanvasRenderingContext2D | null = null;
	// 绘制模式 fill: 填充绘制 stroke: 描边绘制
	canvasMode: 'fill' | 'stroke' = 'fill';
	// 绘制样式
	styleObj: {
		color: string;
		lineWidth: number;
	} = {
		color: '#00a8ff', // 颜色
		lineWidth: 2 // 线宽
	};
	// 矩形列表源数据
	originBlockArr: BlockInfoType[] = [];
	// 矩形列表数据
	blockArr: BlockInfoType[] = [];
	// 当前鼠标所在的块
	currentBlock: BlockInfoType | null = null;
	// 展示的当前时间
	currentNumber = '0';
	// tooltip dom元素
	tooltip: HTMLDivElement | null = null;
	// tooltip中数字dom
	numDom: HTMLSpanElement | null = null;
	// 100%处的数字
	total = 0;
	// 0%处的数字
	begin = 0;
	// 格式化类型
	formatType: 'time' | 'normal' = 'time';
	// 定时器
	timer: ReturnType<typeof setTimeout> | undefined = undefined;
	// 鼠标移动时是否扩大较窄的块
	isAmplify = true;
	// 事件标识
	eventFlag = false;
	// 点击事件方法
	eventFunction:
		| ((e: MouseEvent, currentBlock: BlockInfoType | null, ...args: unknown[]) => void)
		| null = null;
	// 点击事件参数
	eventParams: unknown[] = [];

	constructor(canvasWrapper: HTMLDivElement | null) {
		if (!canvasWrapper) {
			throw new Error('请传入正确的容器dom！');
		}
		this.canvasWrapper = canvasWrapper;
	}

	// 创建画布 styleObj: 样式对象(不要再传入width、height)
	async createCanvas(styleObj: Record<string, string> = {}): Promise<void> {
		const { canvasWrapper } = this;
		if (canvasWrapper) {
			const canvasWrapperWidth = canvasWrapper.offsetWidth;
			const canvasWrapperHeight = canvasWrapper.offsetHeight;

			/* 创建包裹元素 */
			const areaCanvasWrap = document.createElement('div');
			areaCanvasWrap.className = 'area-canvas-wrap';
			areaCanvasWrap.style.position = 'relative';
			areaCanvasWrap.style.zIndex = '999';
			areaCanvasWrap.style.height = '100%';
			canvasWrapper.appendChild(areaCanvasWrap);
			this.areaCanvasWrap = areaCanvasWrap;

			const canvas = document.createElement('canvas');
			canvas.className = 'region-canvas';
			/* 设置canvas样式 */
			canvas.style.overflow = 'hidden';
			// canvas宽高默认与容器一致
			canvas.style.width = '100%';
			canvas.width = canvasWrapperWidth;
			canvas.style.height = `${canvasWrapperHeight}px`;
			canvas.height = canvasWrapperHeight;
			// 遍历对象，设置canvas样式
			Object.keys(styleObj).forEach((key) => {
				canvas.style.setProperty(key, styleObj[key]);
			});

			areaCanvasWrap.appendChild(canvas);
			this.canvas = canvas;
			this.ctx = canvas.getContext('2d');
		}
	}

	// 在指定位置渲染dom结构 htmlDom: 传入的dom结构 leftOffset: 距离左侧距离 bottomOffset: 距离底部距离
	renderDomAtSpecifiedPosition(htmlDom: string, leftOffset: number, bottomOffset: number): void {
		// 创建包裹元素
		const pointItem = document.createElement('div');
		pointItem.className = 'point-item';
		// 设置样式
		pointItem.style.position = 'absolute';
		pointItem.style.bottom = `${bottomOffset}px`;
		pointItem.style.left = `${leftOffset}px`;
		pointItem.style.pointerEvents = 'none';
		// 嵌入html
		pointItem.innerHTML = htmlDom;

		if (this.areaCanvasWrap) {
			this.areaCanvasWrap.appendChild(pointItem);
		} else {
			throw new Error('请先创建画布！');
		}
	}

	// 在指定位置渲染矩形结构
	renderRectAtSpecifiedPosition(styleObj: Record<string, string> = {}): void {
		// 创建包裹元素
		const sectionItem = document.createElement('div');
		sectionItem.className = 'section-item';
		// 设置样式
		sectionItem.style.position = 'absolute';
		sectionItem.style.pointerEvents = 'none';
		// 遍历对象，设置canvas样式
		Object.keys(styleObj).forEach((key) => {
			sectionItem.style.setProperty(key, styleObj[key]);
		});

		if (this.areaCanvasWrap) {
			this.areaCanvasWrap.appendChild(sectionItem);
		} else {
			throw new Error('请先创建画布！');
		}
	}

	// 设置绘制模式
	setCanvasMode(canvasMode: 'fill' | 'stroke'): void {
		if (canvasMode !== 'fill' && canvasMode !== 'stroke') {
			throw new Error('请传入正确的绘制模式！');
		}
		this.canvasMode = canvasMode;
	}

	// 设置样式
	setStyle(styleObj: { color?: string; lineWidth?: number }): void {
		if (Object.prototype.toString.call(styleObj) !== '[object Object]') {
			throw new Error('请传入正确的样式对象！');
		}
		const { color, lineWidth } = styleObj;
		if (color) {
			this.styleObj.color = color;
		}
		if (lineWidth) {
			this.styleObj.lineWidth = lineWidth;
		}
	}

	/* 在指定坐标处绘制矩形
     rectList:[{
        x: 0 矩形左上角与canvas左上角水平距离
        y: 0 矩形左上角与canvas左上角垂直距离
        width: 10 矩形宽度
        height: 10 矩形高度
     }]
  */
	drawRect(rectList: BlockInfoType[], isAmplify = true): void {
		this.originBlockArr = JSON.parse(JSON.stringify(rectList));
		this.blockArr = JSON.parse(JSON.stringify(rectList));
		this.isAmplify = isAmplify;
		try {
			rectList.forEach((rect) => {
				const { x, y, width, height } = rect;
				if (!x && x !== 0) {
					throw new Error('x坐标不能为空！');
				} else if (!y && y !== 0) {
					throw new Error('y坐标不能为空！');
				} else if (!width) {
					throw new Error('宽度不能为空！');
				} else if (!height) {
					throw new Error('高度不能为空！');
				}
				if (this.canvasMode === 'fill' && this.ctx) {
					// 填充矩形
					this.ctx.beginPath();
					this.ctx.fillStyle = this.styleObj.color; // 填充颜色
					this.ctx.fillRect(x, y, width, height); // 填充矩形位置和大小
					this.ctx.closePath();
				} else if (this.canvasMode === 'stroke' && this.ctx) {
					// 描边矩形
					this.ctx.beginPath();
					this.ctx.strokeStyle = this.styleObj.color; // 描边颜色
					this.ctx.lineWidth = this.styleObj.lineWidth; // 设置线宽
					this.ctx.strokeRect(x, y, width, height); // 描边矩形位置和大小
					this.ctx.closePath();
				}
			});
		} catch (err) {
			if (err !== 'StopIteration') {
				throw err;
			}
		}
	}

	/* 生成tooltip，在鼠标移动时会显示tooltip 
   	 	type: 'time' 按照时间戳转小时：分钟格式化 'normal'不做任何处理直接展示
    	total: number canvas100%处的数字，用于计算鼠标当前位置的数字
		begin: 起点的数字
  	*/
	createTooltip(
		type: 'time' | 'normal',
		total: number,
		begin: number,
		styleObj: Record<string, string> = {}
	): void {
		if (!type || !total) {
			throw new Error('请传入正确的参数！');
		}
		const tooltip = document.createElement('div');
		tooltip.className = 'canvas-tooltip';
		// 遍历对象，设置canvas样式
		Object.keys(styleObj).forEach((key) => {
			tooltip.style.setProperty(key, styleObj[key]);
		});
		// 在内部添加span
		const numDom: HTMLSpanElement = document.createElement('span');
		numDom.className = 'tooltip-num';
		numDom.innerHTML = this.currentNumber;
		this.numDom = numDom;
		tooltip.appendChild(numDom);
		// 在内部添加箭头样式
		const arrowDom: HTMLSpanElement = document.createElement('span');
		arrowDom.className = 'arrow';
		// 在传入的样式对象中获取颜色
		const { backgroundColor } = styleObj;
		arrowDom.style.position = 'absolute';
		arrowDom.style.bottom = '-8px';
		arrowDom.style.left = '50%';
		arrowDom.style.transform = 'translateX(-50%)';
		arrowDom.style.width = '0';
		arrowDom.style.height = '0';
		arrowDom.style.border = `4px solid ${backgroundColor}`;
		arrowDom.style.borderColor = `${backgroundColor}  transparent transparent transparent`;
		tooltip.appendChild(arrowDom);

		if (this.areaCanvasWrap) {
			this.areaCanvasWrap.appendChild(tooltip);
		} else {
			throw new Error('请先创建画布！');
		}
		this.tooltip = tooltip;
		this.total = total;
		this.begin = begin;
		this.formatType = type;
	}

	// canvas绑定鼠标事件（移入、移出、移动）
	canvasBindEvent(): void {
		// 给页面绑定鼠标移动事件，当移动后，设置状态为true，这时才能触发canvas移入移出事件，防止鼠标在canvas边界不停触发移入移出事件导致的闪烁
		document.addEventListener('mousemove', this._documentMousemoveEvent);

		if (this.canvas) {
			// canvas绑定鼠标移入事件
			this.canvas.addEventListener('mouseover', this._mouseoverEvent);

			// canvas绑定鼠标移出事件
			this.canvas.addEventListener('mouseout', this._mouseoutEvent);

			// canvas绑定鼠标移动事件
			this.canvas.addEventListener('mousemove', this._mousemoveEvent);
		}
	}

	// 移除绑定鼠标事件
	removeCanvasBindEvent(): void {
		document.removeEventListener('mousemove', this._documentMousemoveEvent);

		if (this.canvas) {
			this.canvas.removeEventListener('mouseover', this._mouseoverEvent);
			this.canvas.removeEventListener('mouseout', this._mouseoutEvent);
			this.canvas.removeEventListener('mousemove', this._mousemoveEvent);
		}
	}

	// 文档鼠标移动事件
	_documentMousemoveEvent = (): void => {
		this.eventFlag = true;
	};

	// canvas鼠标进入事件
	_mouseoverEvent = (e: MouseEvent): void => {
		e.stopPropagation();
		if (this.eventFlag) {
			this.eventFlag = false;
			// 显示提示框
			this._mouseEvent(e);
			// 5ms延迟，防止闪烁
			setTimeout(() => {
				if (this.tooltip) {
					this.tooltip.style.display = 'block';
				}
			}, 5);
		}
	};

	// canvas鼠标移出事件
	_mouseoutEvent = (e: MouseEvent): void => {
		e.stopPropagation();
		this.eventFlag = false;
		// 显示提示框
		this._mouseEvent(e);
		setTimeout(() => {
			if (this.tooltip) {
				this.tooltip.style.display = 'none';
			}
			// canvas恢复原样
			this.currentBlock = null;
			this.blockArr = JSON.parse(JSON.stringify(this.originBlockArr));
			this._resetTeachingDurationChart();
		}, 5);
	};

	// canvas鼠标移动事件
	_mousemoveEvent = (e: MouseEvent): void => {
		e.stopPropagation();
		this.eventFlag = true;
		this._mouseEvent(e);
		// 5ms延迟，防止闪烁
		setTimeout(() => {
			if (this.tooltip) {
				this.tooltip.style.display = 'block';
			}
		}, 5);
	};

	// 鼠标事件
	_mouseEvent(e: MouseEvent): void {
		const _width = this.canvas?.clientWidth ?? 0;
		// 兼容firefox，重新赋值
		const _offsetX = e.offsetX > _width ? _width : e.offsetX;
		clearTimeout(this.timer);
		this.timer = setTimeout(() => {
			// 当前位置的数字
			const _currentNumber =
				(_offsetX / _width) * this.total < 0
					? this.begin
					: (_offsetX / _width) * this.total + this.begin;
			if (this.formatType === 'time') {
				this.currentNumber = formatTime(_currentNumber, 'HH:mm:ss');
			} else if (this.formatType === 'normal') {
				this.currentNumber = String(_currentNumber);
			}
			// 计算偏移
			if (this.numDom) {
				this.numDom.innerHTML = this.currentNumber;
			}
			if (this.tooltip) {
				this.tooltip.style.left = `${_offsetX}px`;
			}
			/* 针对宽度很小的块优化 */
			if (this.isAmplify) {
				// 判断当前鼠标所在的块
				const _block = this.blockArr.find(
					(item) => _offsetX >= item.x && _offsetX <= item.x + item.width
				);
				const _index = _block?.index;
				// 计算调整后的数据
				if (_index !== undefined && _block && _block.width < 10) {
					this.currentBlock = _block;
					this.blockArr = JSON.parse(JSON.stringify(this.originBlockArr));
					const _leftOffset = Math.floor((10 - this.blockArr[_index]?.width) / 2);
					const _rightOffset = 10 - this.blockArr[_index].width - _leftOffset;
					this.blockArr[_index].x -= _leftOffset;
					this.blockArr[_index].width = 10;
					this.blockArr.forEach((item) => {
						if (item.index < _index) {
							item.x -= _leftOffset;
						} else if (item.index > _index) {
							item.x += _rightOffset;
						}
					});
					this._resetTeachingDurationChart();
				} else if ((!_block || _block.width > 10) && this.currentBlock) {
					this.currentBlock = null;
					this.blockArr = JSON.parse(JSON.stringify(this.originBlockArr));
					this._resetTeachingDurationChart();
				}
			}
		}, 5);
	}

	// canvas绑定点击事件
	canvasBindClickEvent<T extends unknown[]>(
		eventFunction: (e: MouseEvent, currentBlock: BlockInfoType | null, ...args: T) => void,
		...params: T
	): void {
		if (!eventFunction) {
			throw new Error('请传入回调函数！');
		}
		this.eventFunction = eventFunction as unknown as (
			e: MouseEvent,
			currentBlock: BlockInfoType | null,
			...args: unknown[]
		) => void;
		this.eventParams = params as unknown as unknown[];
		if (this.canvas) {
			this.canvas.addEventListener('click', this._canvasClickEvent);
		} else {
			throw new Error('请先创建画布！');
		}
	}

	// canvas解绑点击事件
	removeCanvasBindClickEvent(): void {
		if (this.canvas) {
			this.canvas.removeEventListener('click', this._canvasClickEvent);
		} else {
			throw new Error('请先创建画布！');
		}
	}

	// canvas点击事件
	_canvasClickEvent = (e: MouseEvent): void => {
		if (this.eventFunction) {
			this.eventFunction(e, this.currentBlock, ...this.eventParams);
		}
	};

	// 重新绘制语音时长图
	_resetTeachingDurationChart(): void {
		if (this.ctx && this.canvas) {
			this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
		}
		this.blockArr.forEach((item) => {
			if (this.ctx) {
				this.ctx.fillStyle = item.color;
				this.ctx.fillRect(item.x, item.y, item.width, item.height);
			}
		});
	}
}
